package com.xb.system.sharding;

import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;

import java.util.Collection;
import java.util.Properties;

/**
 * 自定义 MurmurHash3 分片算法
 * 与 Python mmh3.hash() 和核验功能的实现保持完全一致
 */
public class MurmurHash3ShardingAlgorithm implements StandardShardingAlgorithm<String> {

    private static final int SHARDING_COUNT = 200;
    private Properties props;

    @Override
    public void init(Properties props) {
        this.props = props;
    }

    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<String> shardingValue) {
        String phone = shardingValue.getValue();
        
        // 使用与 Python mmh3.hash() 一致的 MurmurHash3 实现
        int hashValue = murmurHash3(phone);
        
        // 与 Python 的 abs(hash_value) % 200 保持一致
        int tableIndex = Math.abs(hashValue) % SHARDING_COUNT;
        
        String targetTable = "data_" + tableIndex;
        
        // 从可用表名中找到匹配的表
        for (String tableName : availableTargetNames) {
            if (tableName.endsWith(targetTable)) {
                return tableName;
            }
        }
        
        return availableTargetNames.iterator().next();
    }

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<String> shardingValue) {
        // 范围查询返回所有表
        return availableTargetNames;
    }

    @Override
    public Properties getProps() {
        return props;
    }

    @Override
    public String getType() {
        return "MURMUR_HASH3";
    }

    /**
     * MurmurHash3算法实现
     * 与 Python mmh3.hash() 和 UsersServiceImpl 的实现完全一致
     */
    private int murmurHash3(String key) {
        if (key == null || key.isEmpty()) {
            return 0;
        }
        
        byte[] data = key.getBytes();
        int length = data.length;
        int seed = 0;
        
        // MurmurHash3常量
        int c1 = 0xcc9e2d51;
        int c2 = 0x1b873593;
        int r1 = 15;
        int r2 = 13;
        int m = 5;
        int n = 0xe6546b64;
        
        int hash = seed;
        int nblocks = length / 4;
        
        // body
        for (int i = 0; i < nblocks; i++) {
            int k = getblock(data, i * 4);
            k *= c1;
            k = rotl32(k, r1);
            k *= c2;
            
            hash ^= k;
            hash = rotl32(hash, r2);
            hash = hash * m + n;
        }
        
        // tail
        int tail = nblocks * 4;
        int k1 = 0;
        switch (length & 3) {
            case 3:
                k1 ^= (data[tail + 2] & 0xff) << 16;
            case 2:
                k1 ^= (data[tail + 1] & 0xff) << 8;
            case 1:
                k1 ^= (data[tail] & 0xff);
                k1 *= c1;
                k1 = rotl32(k1, r1);
                k1 *= c2;
                hash ^= k1;
        }
        
        // finalization
        hash ^= length;
        hash = fmix(hash);
        
        return hash;
    }
    
    private int getblock(byte[] data, int i) {
        return ((data[i] & 0xff) | 
                ((data[i + 1] & 0xff) << 8) | 
                ((data[i + 2] & 0xff) << 16) | 
                ((data[i + 3] & 0xff) << 24));
    }
    
    private int rotl32(int x, int r) {
        return (x << r) | (x >>> (32 - r));
    }
    
    private int fmix(int h) {
        h ^= h >>> 16;
        h *= 0x85ebca6b;
        h ^= h >>> 13;
        h *= 0xc2b2ae35;
        h ^= h >>> 16;
        return h;
    }
}

